package com.clp.protocol.iec104.apdu.asdu.infoobj;

import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.InfoElem;
import com.clp.protocol.iec104.apdu.asdu.infoobj.qua.Qua;
import com.clp.protocol.iec104.definition.ConstVal;
import com.clp.protocol.iec104.definition.TypeTag;
import com.clp.protocol.core.pdu.nbytepdu.NBytePduClip;
import com.clp.protocol.core.pdu.ByteToStringFormat;
import com.clp.protocol.core.pdu.nbytepdu.time2a.Time2a;
import com.clp.protocol.core.utils.ByteUtil;
import io.netty.buffer.ByteBuf;
import lombok.Getter;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 地址不连续的信息体对象
 */
@Getter
public class NcInfoObj extends InfoObj {
    /**
     * 信息元素地址（3字节）
     */
    private int addr;
    /**
     * 信息内容
     */
    private InfoElem infoElem;
    /**
     * 限定词（如果是遥测限定词会保存品质描述词）
     */
    private Qua qua;
    /**
     * 时标
     */
    private Time2a time2a;

    public NcInfoObj(TypeTag typeTag) {
        this(typeTag, -1, null, null, null);
    }

    public NcInfoObj(TypeTag typeTag, int addr, InfoElem infoElem, Qua qua, Time2a time2a) {
        super(typeTag);
        this.addr = addr;
        this.infoElem = infoElem;
        this.qua = qua;
        this.time2a = time2a;
    }

    @Override
    public NcInfoObj refreshFrom(ByteBuf buf) {
        checkTypeTag();
        TypeTag typeTag = typeTag();
        // 1、设置信息元素地址（一定有）
        byte[] bytes = new byte[ConstVal.INFO_ELEM_ADDRESS_LEN];
        buf.readBytes(bytes);
        this.addr = ByteUtil.bytes3ToIntLE(bytes);
        // 2、设置信息元素内容（如果有）
        if (typeTag.hasInfoElem()) {
            this.infoElem = typeTag.newInvalidInfoElem().refreshFrom(buf);
        }
        // 3、设定限定词（如果有）
        if (typeTag.hasQua()) {
            this.qua = typeTag.newInvalidQua().refreshFrom(buf);
        }
        // 4、设定时标（如果有）
        if (typeTag.hasTime2a()) {
            try {
                buf.markReaderIndex();
                this.time2a = Time2a.newInvalidCP56Time2a().refreshFrom(buf);
            } catch (Exception e) {
                try {
                    buf.resetReaderIndex();
                    this.time2a = Time2a.newInvalidCP32Time2a().refreshFrom(buf);
                } catch (Exception e2) {
                    e.printStackTrace();
                }
                e.printStackTrace();
            }
        }
        return this;
    }

    @Override
    public boolean isValid() {
        if (!hasTypeTag()) return false;
        if (addr < 0) return false;
        if (typeTag().hasInfoElem() && (infoElem == null || !infoElem.isValid())) return false;
        if (typeTag().hasQua() && (qua == null || !qua.isValid())) return false;
        return !typeTag().hasTime2a() || (time2a != null && time2a.isValid());
    }

    @Override
    public void writeBytesTo(ByteBuf buf) {
        TypeTag typeTag = typeTag();
        // 不连续的信息体需要写入地址值
        buf.writeBytes(ByteUtil.intToBytes3LE(addr));
        // 写入信息元素内容（如果有）
        if (typeTag.hasInfoElem()) {
            infoElem.writeBytesTo(buf);
        }
        // 写入限定词的值（如果有）
        if (typeTag.hasQua()) {
            qua.writeBytesTo(buf);
        }
        // 获取时标（如果有）
        if (typeTag.hasTime2a()) {
            time2a.writeBytesTo(buf);
        }
    }

    @Override
    public void writeFormattedByteStringsTo(StringBuilder sb, String frameClipBytesSeparator, String byteSeparator, ByteToStringFormat byteFormat) {
        byte[] addrBytes = ByteUtil.intToBytes3LE(addr);
        sb.append(byteFormat.format(addrBytes[0]));
        for (int i = 1; i < addrBytes.length; i++) {
            sb.append(byteSeparator).append(byteFormat.format(addrBytes[i]));
        }

        TypeTag typeTag = typeTag();
        List<NBytePduClip<?>> childClips = new ArrayList<>();
        if (typeTag.hasInfoElem()) {
            childClips.add(infoElem);
        }
        if (typeTag.hasQua()) {
            childClips.add(qua);
        }
        if (typeTag.hasTime2a()) {
            childClips.add(time2a);
        }
        for (NBytePduClip<?> childClip : childClips) {
            sb.append(frameClipBytesSeparator);
            childClip.writeFormattedByteStringsTo(sb, frameClipBytesSeparator, byteSeparator, byteFormat);
        }
    }

    @Override
    public void writeSimpleDescriptionTo(StringBuilder sb) {
        sb.append("addr：").append(addr).append("， infoElem：").append(infoElem).append(", qua：").append(qua).append(", time2a：").append(time2a);
    }

    @Override
    public void writeDetailDescriptionTo(StringBuilder sb) {
        sb.append("信息体地址：").append(addr).append("， 信息内容：").append(infoElem).append(", 限定词：").append(qua).append(", 时标：").append(time2a);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        NcInfoObj ncInfoObj = (NcInfoObj) o;
        return addr == ncInfoObj.addr && Objects.equals(infoElem, ncInfoObj.infoElem) && Objects.equals(qua, ncInfoObj.qua) && Objects.equals(time2a, ncInfoObj.time2a);
    }

    @Override
    public int hashCode() {
        return Objects.hash(addr, infoElem, qua, time2a);
    }
}
